﻿namespace DoNet.Common.Cache
{
    /// <summary>
    /// 随机、轮询、权重策略
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public class WeightedPloy<T> where T : class
    {
        /*
        // 初始化数据
        string[] ids = { "Server1", "Server2", "Server3", "Server4" };
        int[] weights = { 10, 20, 30, 40 };

        Span<string> idSpan = ids.AsSpan();
        Span<int> weightSpan = weights.AsSpan();

        var selector0 = new WeightedPloy<string>(idSpan, weightSpan);
        Console.WriteLine(selector0.GetNextRandom());
        Console.WriteLine(selector0.GetNextRandom());
        Console.WriteLine(selector0.GetNextRandom());
        Console.WriteLine(selector0.GetNextRandom());
        Console.WriteLine(selector0.GetNextRandom());
        Console.WriteLine(selector0.GetNextRandom());
        Console.WriteLine(selector0.GetNextRandom());
        Console.WriteLine(selector0.GetNextRandom());
        Console.WriteLine(selector0.GetNextRandom());
        Console.WriteLine(selector0.GetNextRandom());
        Console.WriteLine("以上是随机策略");

        var selector2 = new WeightedPloy<string>(idSpan, weightSpan);
        Console.WriteLine(selector2.GetNextWeightMinusRandom());
        Console.WriteLine(selector2.GetNextWeightMinusRandom());
        Console.WriteLine(selector2.GetNextWeightMinusRandom());
        Console.WriteLine(selector2.GetNextWeightMinusRandom());
        Console.WriteLine(selector2.GetNextWeightMinusRandom());
        Console.WriteLine(selector2.GetNextWeightMinusRandom());
        Console.WriteLine(selector2.GetNextWeightMinusRandom());
        Console.WriteLine(selector2.GetNextWeightMinusRandom());
        Console.WriteLine(selector2.GetNextWeightMinusRandom());
        Console.WriteLine(selector2.GetNextWeightMinusRandom());
        Console.WriteLine("以上是概率累减实现权重随机策略");

        var selector12 = new WeightedPloy<string>(idSpan, weightSpan);
        Console.WriteLine(selector12.GetNextWeightAddRandom());
        Console.WriteLine(selector12.GetNextWeightAddRandom());
        Console.WriteLine(selector12.GetNextWeightAddRandom());
        Console.WriteLine(selector12.GetNextWeightAddRandom());
        Console.WriteLine(selector12.GetNextWeightAddRandom());
        Console.WriteLine(selector12.GetNextWeightAddRandom());
        Console.WriteLine(selector12.GetNextWeightAddRandom());
        Console.WriteLine(selector12.GetNextWeightAddRandom());
        Console.WriteLine(selector12.GetNextWeightAddRandom());
        Console.WriteLine(selector12.GetNextWeightAddRandom());
        Console.WriteLine("以上是概率累加实现权重随机策略");

        var selector = new WeightedPloy<string>(idSpan, weightSpan);
        Console.WriteLine(selector.GetNextPolling());
        Console.WriteLine(selector.GetNextPolling());
        Console.WriteLine(selector.GetNextPolling());
        Console.WriteLine(selector.GetNextPolling());
        Console.WriteLine(selector.GetNextPolling());
        Console.WriteLine(selector.GetNextPolling());
        Console.WriteLine(selector.GetNextPolling());
        Console.WriteLine(selector.GetNextPolling());
        Console.WriteLine(selector.GetNextPolling());
        Console.WriteLine(selector.GetNextPolling());
        Console.WriteLine("以上是轮询策略");

        var selector14 = new WeightedPloy<string>(idSpan, weightSpan);
        Console.WriteLine(selector14.GetNextWeightPolling());
        Console.WriteLine(selector14.GetNextWeightPolling());
        Console.WriteLine(selector14.GetNextWeightPolling());
        Console.WriteLine(selector14.GetNextWeightPolling());
        Console.WriteLine(selector14.GetNextWeightPolling());
        Console.WriteLine(selector14.GetNextWeightPolling());
        Console.WriteLine(selector14.GetNextWeightPolling());
        Console.WriteLine(selector14.GetNextWeightPolling());
        Console.WriteLine(selector14.GetNextWeightPolling());
        Console.WriteLine(selector14.GetNextWeightPolling());
        Console.WriteLine("以上是权重策略"); 
        */
        /// <summary>
        /// 缓存Key
        /// </summary>
        private static string keyName = "WeightedList";
        /// <summary>
        /// 缓存列表
        /// </summary>
        private static ListCacheManager<string, (T, int)> _cache = new ListCacheManager<string, (T, int)>();
        private volatile int tempWeight = 0; //平滑加权
        private int _currentIndex = -1;
        private readonly Random _random = Random.Shared; //线程安全版本

        /// <summary>
        /// 
        /// </summary>
        /// <param name="items"></param>
        /// <param name="weights"></param>
        public WeightedPloy(Span<T> items, Span<int> weights, string suffix = "")
        {
            _cache.RemoveList(keyName);
            for (int i = 0; i < items.Length; i++)
            {
                _cache.AppendToList(keyName, (items[i], weights[i]));
            }
        }

        /// <summary>
        /// 随机（随机策略）
        /// </summary>
        /// <returns></returns>
        public T GetNextRandom()
        {
            var snapshot = _cache.GetListCopy(keyName).ToArray();
            if (snapshot.Length > 0)
            {
                _currentIndex = _random.Next(0, snapshot.Length - 1);
                int index = 0;
                foreach (var (key, weight) in snapshot)
                {
                    if (index == _currentIndex)
                    {
                        return key;
                    }
                    index++;
                }
            }
            return default!;
        }

        /// <summary>
        /// 概率累减实现权重随机 （随机策略，权重策略）
        /// </summary>
        /// <returns></returns>
        public T GetNextWeightMinusRandom()
        {
            var snapshot = _cache.GetListCopy(keyName).ToArray();
            if (snapshot.Length > 0)
            {
                var totalWeight = snapshot.Sum(x => x.Item2);
                var random = _random.Next(totalWeight);
                int index = 0;
                foreach (var (key, weight) in snapshot)
                {
                    if (random < weight)
                    {
                        _currentIndex = index;
                        return key;
                    }
                    random -= weight;
                    index++;
                }
            }
            return default!;
        }

        /// <summary>
        /// 概率累加实现权重随机 （随机策略，权重策略）
        /// </summary>
        /// <returns></returns>
        public T GetNextWeightAddRandom()
        {
            var snapshot = _cache.GetListCopy(keyName).ToArray();
            if (snapshot.Length > 0)
            {
                var totalWeight = snapshot.Sum(x => x.Item2);
                int randomValue = Random.Shared.Next(totalWeight);
                int cumulative = 0;

                int index = 0;
                foreach (var (key, weight) in snapshot)
                {
                    cumulative += weight;
                    if (randomValue < cumulative)
                    {
                        _currentIndex = index;
                        return key;
                    }
                    index++;
                }
            }
            return default!;
        }

        //轮询策略包含基础轮询和加权轮询两种实现
        /// <summary>
        /// 基础轮询（轮询策略）
        /// </summary>
        /// <returns></returns>
        public T GetNextPolling()
        {
            var snapshot = _cache.GetListCopy(keyName).ToArray();
            if (snapshot.Length > 0)
            {
                _currentIndex = (_currentIndex + 1) % snapshot.Length;
                int index = 0;
                foreach (var (key, weight) in snapshot)
                {
                    if (index == _currentIndex)
                    {
                        return key;
                    }
                    index++;
                }
            }
            return default!;
        }

        /// <summary>
        /// 加权轮询
        /// 平滑加权轮询（轮询策略，权重策略）
        /// </summary>
        /// <returns></returns>
        public T GetNextWeightPolling()
        {
            var snapshot = _cache.GetListCopy(keyName).ToArray();
            if (snapshot.Length > 0)
            {
                var totalWeight = snapshot.Sum(x => x.Item2);
                var minWeight = snapshot.Min(x => x.Item2);
                while (true)
                {
                    _currentIndex = (_currentIndex + 1) % snapshot.Length;
                    if (_currentIndex == 0)
                    {
                        tempWeight -= minWeight;
                        if (tempWeight <= 0)
                            tempWeight = totalWeight;
                    }

                    int index = 0;
                    foreach (var (key, weight) in snapshot)
                    {
                        if (index == _currentIndex)
                        {
                            if (weight >= tempWeight)
                                return key;
                        }
                        index++;
                    }
                }
            }
            return default!;
        }
    }
}
